/*
 *  Copyright 2005 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.webapp.taglib.core.html;

import org.apache.struts.taglib.html.Constants;
import org.apache.struts.taglib.TagUtils;
import org.apache.commons.validator.GenericValidator;

import javax.servlet.jsp.JspException;
import javax.servlet.jsp.PageContext;
import javax.servlet.jsp.tagext.JspFragment;
import java.io.IOException;
import java.io.StringWriter;

import com.blandware.atleap.webapp.taglib.core.BaseHandlerTag;


/**
 * <p>Tag for 'cancel' element. Originally taken from Struts CancelTag. This tag
 * may also generate a 'back' button (that is controlled with <b>type</b>
 * attribute.</p>
 * <p>
 * Following attributes are supported:
 * <ul>
 * <li>
 * <b>property</b> - name of the generated input field. The default value for cancel type is
 * <code>org.apache.struts.taglib.html.Constants.CANCEL_PROPERTY</code>. If button
 * is actually back button and no <b>property</b> is specified, then
 * <code>com.blandware.atleap.taglib.html.BACK</code> will be used.
 * </li>
 * <li>
 * <b>value</b> - value of the button label
 * </li>
 * <li>
 * <b>type</b> - type of cancel button: regular cancel (if 'cancel') or back
 * button (if 'back'). If <b>type</b> is neither 'cancel' nor 'back', it's
 * assumed to be 'cancel' (which is default).
 * </li>
 * <li>
 * <b>disableButtonOnCancel</b> - whether to disable cancel button after it was pressed
 * to prevent 'multiple cancel'. This can be 'true' or 'false',
 * default is 'false'.
 * </li>
 * </ul>
 * Some attributes are inherited from {@link BaseHandlerTag}.
 * </p>
 * <p>
 * This tag can also have body, its content will be rendered as submit button
 * label. <b>Value</b> has precedence over body. If none of <b>value</b> and
 * tag body has been specified, button label will be 'Cancel' or 'Back'.
 * </p>
 * <p>
 * Note: though <b>property</b> attribute is accessible, you don't usually need
 * to specify it if you want to make this button behave like 'cancel' or 'back'
 * button: if <b>property</b> is omitted, it will be generated automatically
 * (depending on <b>type</b>), so the system will process submittion of such button
 * as expected.
 * </p>
 *
 * <p><a href="CancelTag.java.html"><i>View source</i></a></p>
 *
 * @author Roman Puchkovskiy <a href="mailto:roman.puchkovskiy@blandware.com">
 *         &lt;roman.puchkovskiy@blandware.com&gt;</a>
 * @version $Revision: 1.6 $ $Date: 2005/10/12 13:34:59 $
 * @see com.blandware.atleap.webapp.taglib.core.BaseHandlerTag
 * @see org.apache.struts.taglib.html.CancelTag
 * @jsp.tag name="cancel"
 * body-content="scriptless"
 */
public class CancelTag extends BaseHandlerTag {
    /**
     * Name of key under which 'back' property is stored in context
     */
    public static final String BACK_PROPERTY = "com.blandware.atleap.taglib.html.BACK";

    /**
     * The property name of the generated button.
     */
    protected String property = null;

    /**
     * The value of the button label.
     */
    protected String value = null;

    /**
     * Whether to disable submit button after pressing it (to prevent multiple
     * cancelling)
     */
    protected Boolean disableButtonOnCancel = Boolean.FALSE;

    /**
     * Type of cancel button: 'cancel' or 'back'
     */
    protected String type = "cancel";

    /**
     * Returns the property name.
     *
     * @return property, which is the name of corresponding 'input' tag
     * @jsp.attribute required="false"
     * rtexprvalue="true"
     * type="java.lang.String"
     * description="Name of the 'input' tag"
     */
    public String getProperty() {
        return (property);
    }

    /**
     * Sets the property name.
     *
     * @param property the property name to set
     */
    public void setProperty(String property) {
        this.property = property;
    }


    /**
     * Returns the label value.
     *
     * @return value of the label
     * @jsp.attribute required="false"
     * rtexprvalue="true"
     * type="java.lang.String"
     * description="Label of the button"
     */
    public String getValue() {
        return (value);
    }


    /**
     * Sets the label value.
     *
     * @param value the label value to set
     */
    public void setValue(String value) {
        this.value = value;
    }

    /**
     * Gets a property which specifies whether to disable button after cancel
     *
     * @return whether to disable button after cancel
     * @jsp.attribute required="false"
     * rtexprvalue="true"
     * type="java.lang.Boolean"
     * description="Whether to disable button after cancel"
     */
    public Boolean getDisableButtonOnCancel() {
        return disableButtonOnCancel;
    }

    /**
     * Sets whether to disable button after cancel
     *
     * @param disableButtonOnCancel whether to disable button after cancel
     */
    public void setDisableButtonOnCancel(Boolean disableButtonOnCancel) {
        this.disableButtonOnCancel = disableButtonOnCancel;
    }

    /**
     * Returns type of this button: 'cancel' or 'back'
     *
     * @return type of this button: 'cancel' or 'back'
     * @jsp.attribute required="false"
     * rtexprvalue="true"
     * type="java.lang.String"
     * description="Type of the cancel button: 'cancel' or 'back'"
     */
    public String getType() {
        return type;
    }

    /**
     * Sets type of this button: 'cancel' or 'back'
     *
     * @param type type to set
     */
    public void setType(String type) {
        this.type = type;
    }

    /**
     * Processes the tag.
     *
     * @throws javax.servlet.jsp.JspException if a JSP exception has occurred
     * @throws java.io.IOException is an IO exception has occured
     */
    public void doTag() throws JspException, IOException {
        // Get text from inside of the tag
        String text;
        StringWriter writer = new StringWriter();
        JspFragment body = getJspBody();
        if (body != null) {
            body.invoke(writer);
            text = writer.toString().trim();
        } else {
            text = null;
        }

        // Acquire the label value we will be generating
        String label = value;
        if ((label == null) && (text != null)) {
            label = text;
        }
        if ((label == null) || (label.length() < 1)) {
            label = "back".equalsIgnoreCase(type) ? "Back" : "Cancel";
        }

        // Prepend disabling JS-code to onclick event handler, if needed
        String disablingCode = "this.disabled = true;";
        if (disableButtonOnCancel == null) {
            disableButtonOnCancel = Boolean.FALSE;
        }
        boolean onclickWasntProvided = onclick == null;
        if (disableButtonOnCancel.booleanValue()) {
            if (onclickWasntProvided) {
                onclick = disablingCode;
            } else {
                onclick = disablingCode + onclick;
            }
        }
        // if no onclick event was provided, put in the cancel script
        if (onclickWasntProvided) {
            String setBCancelCode = "bCancel=true;";
            if (onclick == null) {
                onclick = setBCancelCode;
            } else {
                onclick = setBCancelCode + onclick;
            }
        }

        // Prepend JS code that creates a hidden field with same name and value
        // as our cancel button has. This is done because if cancel button
        // gets disabled on form submit, it's value will not be sent to server.
        String addHiddenFieldCode = "createFormElement('input', 'hidden', this.name, this.id, 'true', this.form);";
        onclick = addHiddenFieldCode + onclick;

        if (GenericValidator.isBlankOrNull(property)) {
            if ("back".equalsIgnoreCase(type)) {
                property = BACK_PROPERTY;
            } else {
                property = Constants.CANCEL_PROPERTY;
            }
        }

        // Generate an HTML element
        StringBuffer results = new StringBuffer();
        results.append("<input type=\"submit\"");
        if (property != null) {
            results.append(" name=\"");
            results.append(property);
            results.append("\" ");
        }
        if (label != null) {
            results.append(" value=\"");
            results.append(label);
            results.append("\" ");
        }
        results.append(prepareAttributes());
        results.append("/>");

        // Write HTML-code to page
        PageContext pageContext = (PageContext) getJspContext();
        TagUtils.getInstance().write(pageContext, results.toString());
    }
}
